/*
 *Classe ColorConverter, un controlleur qui fait la conversion de tout les espaces couleurs.
 *
 *Classe ecrite par Sean Blahovici, Hachem Andaloussi et Vincent Larose
 *
 *Derniere mise à jour : 30/09/13
 */

package controller;

import model.CMYKColor;
import model.HSVColor;
import model.RGBColor;

public class ColorConverter {
	private static ColorConverter instance = null;
	
	private ColorConverter() {
		
	}
	
	public static ColorConverter getInstance() {
		if(instance == null) {
			instance = new ColorConverter();
		}
		return instance;
	}
	

	/**
	 * Permet de convertire de RGB a CMYK
	 * CMYK est representatif de la theorie des couleurs soustractives.
	 * On recoit les couleurs RGB de valeurs 1 a 255 et on retourne la meme chose mais converti pour CMYK
	 * Methode basee en partie sur algorithme trouve sur le site suivant : http://www.easyrgb.com/index.php?X=MATH&H=11#text11 et http://www.easyrgb.com/index.php?X=MATH&H=13#text13
	 * @param rgbColor
	 * @return objet CMYKColor
	 */
	public CMYKColor convertRGBtoCMYK(RGBColor rgbColor) {
		        
		//RGB a CMY
		double C = 255 - rgbColor.getRed();
    	double M = 255 - rgbColor.getGreen();
    	double Y = 255 - rgbColor.getBlue();
		
    	//La valeur de K est la plus petite des 3 autres couleurs
    	double K = Math.min(Math.min(C,M), Y);

    	C = C/255;
    	M = M/255;
    	Y = Y/255;
    	K = K/255;
    	
    	//Si noir = max, autres couleurs ne se voient pas. Donc noir dans le cas du CMYK
    	if ( K == 1 ) {
    		C = 0;
    		M = 0;
    		Y = 0;
    	} else {
    		//Ajustement de la teinte des couleurs due au niveau du KeyBlack
    		C = ( C - K ) / ( 1 - K );
    		M = ( M - K ) / ( 1 - K );
    		Y = ( Y - K ) / ( 1 - K );
    	}
    	
    	//On remet les valeurs a 255
    	C *= 255;
    	M *= 255;
    	Y *= 255;
    	K *= 255;
    	    	
        return new CMYKColor((int)C,(int)M,(int)Y,(int)K);
	}
	
	/**
	 * Permet de convertire de CMYK a RGB
	 * Encore une fois, un algorithme de soustraction de couleurs
	 * Methode basee en partie sur algorithme trouve sur le site suivant : http://www.easyrgb.com/index.php?X=MATH&H=12#text12	 
	 * @param cmykColor
	 * @return objet RGBColor
	 */
	public RGBColor convertCMYKtoRGB(CMYKColor cmykColor) {
	 
		//Reajustement de la teinte des couleurs due au niveau du KeyBlack
		int red = ((255-cmykColor.getCyan())*(255-cmykColor.getBlack())) / 255;
    	int green = ((255-cmykColor.getMagenta())*(255-cmykColor.getBlack())) / 255;
    	int blue = ((255-cmykColor.getYellow())*(255-cmykColor.getBlack())) / 255;
        
        return new RGBColor(red, green, blue);
	}
	
	/**
	 * Permet de convertire de RGB a HSV
	 * Methode basee en partie sur algorithme trouve sur le site suivant : https://cours.etsmtl.ca/gti410/ (cours 1 page 32)
	 * La theorie sur ces algorithmes peut etre trouvee sur le site suivant : http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV
	 * @param rgbColor
	 * @return objet HSVColor
	 */
	public HSVColor convertRGBtoHSV(RGBColor rgbColor) {	
		
		System.out.println( " RGB TO HSV IN R : " + rgbColor.getRed() + " G : " + rgbColor.getGreen() + " B : " + rgbColor.getBlue());
		
		double hue = 0;
        double saturation = 0;
        double value = 0;
		
        //RGB de 0 a 255
		double redTmp = rgbColor.getRed();// / 255.0;                     
		double greenTmp = rgbColor.getGreen();// / 255.0;
		double blueTmp= rgbColor.getBlue();// / 255.0;
		
		redTmp = redTmp/255;
		greenTmp = greenTmp/255;
		blueTmp = blueTmp/255;

		double maxVal = Math.max(redTmp, Math.max(greenTmp, blueTmp));
		double minVal = Math.min(redTmp, Math.min(greenTmp, blueTmp));   
		  
		
		//Ce ratio est la difference entre la plus grande et plus petite de  R, G, ou B dans une couleur
		//deltaMinMax = chroma
		double deltaMinMax = maxVal - minVal;            

		value = maxVal;

		if ( maxVal != 0 )                     
		{                          
		   saturation = (deltaMinMax) / value;
		}
		
		//le hue se calculera differenment selon si a valeur de rouge, vert et/ou bleu est egal a la valeur maximale
			if((redTmp==maxVal) && (greenTmp==maxVal)) {
				
	            hue = 5 + (double)((redTmp-blueTmp)/(deltaMinMax));
	            
	        }else if((blueTmp==maxVal)&& (redTmp==maxVal)){ 
	        	
	            hue = 1 - (double)((redTmp-greenTmp)/(deltaMinMax));
	            
	        }else if((greenTmp==minVal)&&(blueTmp==maxVal)) {
	        	
	            hue = 5 - (double)((blueTmp-redTmp)/(deltaMinMax));
	            
	        }else if((redTmp==minVal)&&(blueTmp==maxVal)){
	        	
	            hue = 3 + (double)((blueTmp-greenTmp)/(deltaMinMax));
	            
	        }else if((redTmp==minVal)&&(greenTmp==maxVal)){ 
	        	
	            hue = 3 - (double)((greenTmp-blueTmp)/(deltaMinMax));
	            
	        }else if((blueTmp==minVal)&&(greenTmp==maxVal)) {
	        	
	            hue = 1 + (double)((greenTmp-redTmp)/(deltaMinMax));
	            
	        }else{
	            hue = 0;
	        } 
	        
		   //Pour tout remettre a 360 degres
	        hue = hue * 60;
	        
	        if(hue < 0)
	        {
	    		hue += 360;
	    		
	        } 
		
	    //Remettre a 255
		hue = hue/360 * 255; 
		saturation *= 255;
		value *= 255.0;
		System.out.println( " RGB TO HSV OUT h : " + hue + " S : " + saturation + " V : " + value);
		
		return new HSVColor((int) hue, (int) saturation,(int) value);
	}
	
	/**
	 * Permet de convertire de HSV a RGB
	 * Methode basee en partie sur algorithme trouve sur le site suivant : http://www.easyrgb.com/index.php?X=MATH&H=21#text21
	 * La theorie sur ces algorithmes peut etre trouvee sur le site suivant  : http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV
	 * @param hsvColor
	 * @return objet RGBColor
	 */
	public RGBColor convertHSVtoRGB(HSVColor hsvColor) {

		double S = hsvColor.getSaturation();
		double H = hsvColor.getHue();
		double V = hsvColor.getValue();
		
		//HSV de 0 a 1
		H = H/255; 
		S = S/255;
		V = V/255;
		
		System.out.println("HSVtoRGB : H : " + H + " S: " + S + " V : " + V);
		
		double red;
		double green;
		double blue;
		
		double highVar;
		int lowVar;
		
		double redTemp = 0;
		double blueTemp = 0;
		double greenTemp = 0;
		
		double calcul1;
		double calcul2;
		double calcul3;
		
		if ( S == 0 ){                      
			red = V * 255;
			green = V * 255;
			blue = V * 255;
		}else{
			
			//Puisque le hue varie entre 0 et 360 degres, nous allons verifier sa valeur a chaque 60 degre (donc 360/6)
			//et trouver un point (redTemp, greenTemp, blueTemp) le long des 3 faces du cube RGB, avec le meme hue et chroma que notre couleur
			highVar = H * 6; //H max
		    if ( highVar == 6 ){ 
		    	highVar = 0;    //H must be < 1
		    }
		    lowVar =  (int)Math.floor(highVar);  
		    System.out.println("this is the value of the lowVar : " + lowVar);
		    calcul1 = V * ( 1 - S );
		    calcul2 = V * ( 1 - S * ( highVar - lowVar ) );
		    calcul3 = V * ( 1 - S * ( 1 - ( highVar - lowVar ) ) );

		    switch(lowVar){
			    case 0 : 
				    	redTemp = V; 
				    	greenTemp = calcul3; 
				    	blueTemp = calcul1; 
				    	break;
			    case 1 : 
				    	redTemp = calcul2; 
				    	greenTemp = V; 
				    	blueTemp = calcul1;
				    	break;
			    case 2 : 
				    	redTemp = calcul1 ; 
				    	greenTemp = V; 
				    	blueTemp = calcul3; 
				    	break;
			    case 3 :
				    	redTemp = calcul1 ; 
				    	greenTemp = calcul2 ; 
				    	blueTemp = V;    
				    	break;
			    case 4 :
				    	redTemp = calcul3; 
				    	greenTemp = calcul1; 
				    	blueTemp = V;   
				    	break;
			    default :
				    	redTemp = V; 
				    	greenTemp = calcul1; 
				    	blueTemp = calcul2;
				    	break;
				}
		    	
		    
		    //RGB de 0 a 255
		    red = redTemp * 255;                  
		    green = greenTemp * 255;
		    blue = blueTemp * 255;
		}	
		
		
		System.out.println("HSVtoRGB SORTANT : red : " + red + " green: " + green + " blue : " + blue);
		
		return new RGBColor((int)red,(int)green,(int)blue);
	}

	
}
